package com.ocom.nettyxf.util.sm2;

import com.ocom.nettyxf.util.NumConvertUtil;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.crypto.signers.StandardDSAEncoding;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

import java.io.*;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;

/**
 * @Description: 国密sm2非对称加解密算法帮助类
 * @Author: wucheng
 * @CreateDate: 2020/2/16 16:57
 */
public class SM2Helper {
	
	private static ECCurve.Fp curve;
	
    static{
        Security.addProvider(new BouncyCastleProvider());
		curve = new ECCurve.Fp(SM2Constants.SM2_ECC_Q, // q
				SM2Constants.SM2_ECC_A, // a
				SM2Constants.SM2_ECC_B, // b
				SM2Constants.SM2_ECC_N, // order
				SM2Constants.SM2_ECC_H); // cofactor
    }

    /**
     * 公钥加密
     * @param input 待加密数据
     * @param ecPublicKeyParameters 公钥参数
     * @param mode 加密方式
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] input, ECPublicKeyParameters ecPublicKeyParameters, SM2Engine.Mode mode) throws Exception{
        SM2Engine engine = new SM2Engine(mode);
        ParametersWithRandom parametersWithRandom = new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom());
        engine.init(true, parametersWithRandom);
        return engine.processBlock(input, 0, input.length);
    }

    /**
     * 私钥解密
     * @param input 待解密数据
     * @param ecPrivateKeyParameters 私钥参数
     * @param mode 加密方式
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] input, ECPrivateKeyParameters ecPrivateKeyParameters, SM2Engine.Mode mode) throws Exception{
        SM2Engine engine = new SM2Engine(mode);
        engine.init(false, ecPrivateKeyParameters);
        return engine.processBlock(input, 0, input.length);
    }

    /**
     * 私钥签名
     * @param input 待签名数据
     * @param ecPrivateKeyParameters 私钥数据
     * @param ID 用户标识
     * @return
     * @throws Exception
     */
    public static SM2SignResult sign(byte[] input, ECPrivateKeyParameters ecPrivateKeyParameters, byte[] ID) throws Exception{
        SM2Signer signer = new SM2Signer();
        CipherParameters param;
        if (ID != null && ID.length>0) {
            param = new ParametersWithID(ecPrivateKeyParameters, ID);
        } else {
            param = ecPrivateKeyParameters;
        }
        signer.init(true, param);
        signer.update(input, 0, input.length);
        byte[] sign = signer.generateSignature();
		System.out.println("sign:"+NumConvertUtil.bytesToHexString(sign));
        SM2SignResult SM2SignResult = new SM2SignResult();
        SM2SignResult.decodeStandardDSA(sign);
        return SM2SignResult;
    }

    /**
     * 公钥验证签名
     * @param input 原始数据
     * @param SM2SignResult 签名
     * @param ecPublicKeyParameters 公钥参数
     * @param ID 用户标识
     * @return
     * @throws Exception
     */
    public static boolean verifySign(byte[] input, SM2SignResult SM2SignResult, ECPublicKeyParameters ecPublicKeyParameters, byte[] ID) throws Exception{
        BigInteger signR = new BigInteger(1, SM2SignResult.getSignR());
        BigInteger signS = new BigInteger(1, SM2SignResult.getSignS());
        byte[] sign = StandardDSAEncoding.INSTANCE.encode(SM2Constants.SM2_ECC_N, signR, signS);

        SM2Signer signer = new SM2Signer();
        CipherParameters param;
        if (ID != null && ID.length>0) {
            param = new ParametersWithID(ecPublicKeyParameters, ID);
        } else {
            param = ecPublicKeyParameters;
        }

        signer.init(false, param);
        signer.update(input, 0, input.length);
        return signer.verifySignature(sign);
    }
    
    /**
     * 公钥验证签名
     * @param input 原始数据
     * @param sign 签名
     * @param ecPublicKeyParameters 公钥参数
     * @return
     * @throws Exception
     */
    public static boolean verifySign(byte[] input, byte[] sign, ECPublicKeyParameters ecPublicKeyParameters) throws Exception{
        SM2Signer signer = new SM2Signer();
        signer.init(false, ecPublicKeyParameters);
        signer.update(input, 0, input.length);
        return signer.verifySignature(sign);
    }
    
    /**
	 * 导出公钥到本地
	 * @param publicKey 公钥
	 * @param path 路径
	 */
	public static void exportPublicKey(ECPoint publicKey, String path) {
		File file = new File(path);
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			byte buffer[] = publicKey.getEncoded(false);
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(buffer);
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从本地导入公钥
	 * @param path 路径
	 * @return
	 */
	public static ECPoint importPublicKey(String path) {
		File file = new File(path);
		try {
			if (!file.exists()) {
				return null;
			}
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();

			byte buffer[] = new byte[16];
			int size;
			while ((size = fis.read(buffer)) != -1) {
				baos.write(buffer, 0, size);
			}
			fis.close();
			return curve.decodePoint(baos.toByteArray());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 导出私钥到本地
	 * @param privateKey 私钥
	 * @param path 路径
	 */
	public static void exportPrivateKey(BigInteger privateKey, String path) {
		File file = new File(path);
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
			oos.writeObject(privateKey);
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从本地导入私钥
	 * @param path 路径
	 * @return
	 */
	public static BigInteger importPrivateKey(String path) {
		File file = new File(path);
		try {
			if (!file.exists()) {
				return null;
			}
			FileInputStream fis = new FileInputStream(file);
			ObjectInputStream ois = new ObjectInputStream(fis);
			BigInteger res = (BigInteger) (ois.readObject());
			ois.close();
			fis.close();
			return res;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 公钥解密
	 * 
	 * @param data
	 * @param publicInfoStr
	 * @return
	 * @throws IOException
	 * @throws InvalidCipherTextException
	 */
	public static String decryptData(String data, byte[] publicInfoStr) throws IOException, InvalidCipherTextException {
		final Base64.Decoder decoder64 = Base64.getDecoder();
		String charset = "utf-8";

		AsymmetricBlockCipher cipher = new RSAEngine();
		byte[] encryptDataBytes = decoder64.decode(data);

		// 解密
//		byte[] publicInfoBytes = decoder64.decode(publicInfoStr);
		ASN1Object pubKeyObj = ASN1Primitive.fromByteArray(publicInfoStr);
		AsymmetricKeyParameter pubKey = PublicKeyFactory.createKey(SubjectPublicKeyInfo.getInstance(pubKeyObj));
		// false表示解密
		cipher.init(false, pubKey);

		byte[] decryptDataBytes = cipher.processBlock(encryptDataBytes, 0, encryptDataBytes.length);
		return new String(decryptDataBytes, charset);
	}
	
//	public static void main(String[] args) throws InvalidCipherTextException, IOException {
//		byte[] data = new byte[]{(byte)0xBF,(byte)0x96,(byte)0xA4,(byte)0x48,(byte)0x40,(byte)0x18,(byte)0x11,(byte)0x61,(byte)0x97,(byte)0xB5,(byte)0x25,(byte)0xCA,(byte)0xF5,(byte)0xA8,(byte)0x09,(byte)0x44,(byte)0xB4,(byte)0xCC,(byte)0xB5,(byte)0x98,(byte)0x93,(byte)0xBD,(byte)0x28,(byte)0xB9,(byte)0x92,(byte)0x7A,(byte)0xE0,(byte)0xEF,(byte)0x57,(byte)0x6B,(byte)0x67,(byte)0x1C,(byte)0x99,(byte)0xF2,(byte)0xA2,(byte)0x09,(byte)0x82,(byte)0x36,(byte)0xD2,(byte)0x3E,(byte)0xDC,(byte)0x5B,(byte)0x1A,(byte)0x20,(byte)0x08,(byte)0xD6,(byte)0x12,(byte)0xD9,(byte)0xAA,(byte)0x37,(byte)0x36,(byte)0x6D,(byte)0x37,(byte)0xE7,(byte)0x87,(byte)0x75,(byte)0xBE,(byte)0xF1,(byte)0xEF,(byte)0x4E,(byte)0x83,(byte)0x23,(byte)0xFA};
//		String str = decryptData("20078bb98b45811b29514ed4dd3308acfe8de5ac4fdf1b8e49912f7c291369c81320b0a9a99371d59c85eb531636c32383ca83b9e235847d9fcaea5db826bd353223", data);
//		System.out.println(str);
//	}
}
